using System;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;

namespace FpFrame
{
    public class DissolveTransition : IUITransitionEffect
    {
        private readonly TransitionSO _so;
        private readonly Material _materialTemplate;

        // shader property names (your ShaderGraph should expose these)
        private static readonly int PropFrom = Shader.PropertyToID("_MainTex");
        private static readonly int PropTo = Shader.PropertyToID("_ToTex");
        private static readonly int PropProgress = Shader.PropertyToID("_Progress");
        private static readonly int PropNoise = Shader.PropertyToID("_NoiseTex");
        private static readonly int PropEdgeWidth = Shader.PropertyToID("_EdgeWidth");
        private static readonly int PropEdgeColor = Shader.PropertyToID("_EdgeColor");

        public DissolveTransition(TransitionSO so, Material fallbackMaterial = null)
        {
            _so = so;
            _materialTemplate = null;

            // Prefer material from shader if provided, otherwise use fallbackMaterial (optional)
            if (so.dissolveShader != null)
                _materialTemplate = new Material(so.dissolveShader);
            else if (fallbackMaterial != null)
                _materialTemplate = new Material(fallbackMaterial);
            else
                throw new Exception("DissolveTransition requires a shader or fallback material.");
        }

        public async UniTask PlayTransition(Texture from, Texture to, RawImage screen)
        {
            if (screen == null) throw new ArgumentNullException(nameof(screen));

            // instantiate material so we don't modify template
            var mat = UnityEngine.Object.Instantiate(_materialTemplate);
            mat.SetTexture(PropFrom, from);
            mat.SetTexture(PropTo, to);

            if (_so.noiseTexture != null)
                mat.SetTexture(PropNoise, _so.noiseTexture);

            mat.SetFloat(PropEdgeWidth, _so.edgeWidth);
            mat.SetColor(PropEdgeColor, _so.edgeColor);

            // assign to RawImage
            screen.material = mat;
            screen.texture = null; // ensure RawImage uses material's textures

            float duration = Mathf.Max(0.0001f, _so.duration);
            float t = 0f;

            // drive progress using unscaled time
            while (t < duration)
            {
                t += Time.unscaledDeltaTime;
                float norm = Mathf.Clamp01(t / duration);
                float eased = _so.ease.Evaluate(norm);
                mat.SetFloat(PropProgress, eased);
                await UniTask.Yield();
            }

            // ensure complete
            mat.SetFloat(PropProgress, 1f);

            // end delay a frame to ensure final state drawn
            await UniTask.Yield();

            // cleanup material if needed (keeping the final frame is handled by UIManager hiding the RawImage)
            // Here we destroy the material instance
            UnityEngine.Object.Destroy(mat);

            // Best practice: reset screen material or let UIManager hide RawImage
            screen.material = null;
            screen.texture = null;
        }
    }
}
